﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;

/// <summary>
/// Summary description for ProviderUsage
/// </summary>
public class DomainUsagePro : IComparable<DomainUsagePro>
{
    DataColumn cFromNo = new DataColumn("from_no", typeof(string));
    DataColumn cFromNet = new DataColumn("from_net", typeof(string));
    DataColumn cToNo = new DataColumn("to_no", typeof(string));
    DataColumn cToNet = new DataColumn("to_net", typeof(string));
    DataColumn cDate = new DataColumn("date", typeof(DateTime));
    DataColumn cTime = new DataColumn("time", typeof(double));
    DataColumn cHour = new DataColumn("hour", typeof(int));
    DataColumn cMin = new DataColumn("min", typeof(int));

    public string ProviderString
    {
        get
        {
            switch (Provider)
            {
                case ProviderEnum.AIS:
                    {
                        return "AIS";
                    } break;
                case ProviderEnum.DTAC:
                    {
                        return "DTAC";
                    } break;
                case ProviderEnum.TMOVE:
                    {
                        return "TrueMove";
                    } break;
                case ProviderEnum.All:
                    {
                        return "All Providers";
                    } break;
            }

            return "Other Providers";
        }
    }

    public string FullName
    {
        get
        {
            if (UsageType == UsageDomainTypeEnum.PROVIDER)
            {
                switch (Provider)
                {
                    case ProviderEnum.AIS:
                        {
                            return "AIS";
                        } break;
                    case ProviderEnum.DTAC:
                        {
                            return "DTAC";
                        } break;
                    case ProviderEnum.TMOVE:
                        {
                            return "TrueMove";
                        } break;
                    case ProviderEnum.All:
                        {
                            return "All Providers";
                        } break;
                }

                return "Other Providers";
            }
            else
            {
                return PhoneNum;
                //string result = "{0} ({1})";
                //string pro = "";
                //switch (Provider)
                //{
                //    case ProviderEnum.AIS:
                //        {
                //            pro = "AIS";
                //        } break;
                //    case ProviderEnum.DTAC:
                //        {
                //            pro = "DTAC";
                //        } break;
                //    case ProviderEnum.TMOVE:
                //        {
                //            pro = "TrueMove";
                //        } break;
                //}

                //return string.Format(result, PhoneNum, pro);
            }
        }
    }

    public double Nearest = 0.5;
    public string PhoneNum = "";
    public ProviderEnum Provider = ProviderEnum.All;

    public double[] HourUse = new double[24];
    public Dictionary<string, double[]> RawTotal = new Dictionary<string, double[]>();
    private double Total = 0.0;
    public double TotalDays = 0.0;

    public double TotalMonths
    {
        get
        {
            return Math.Ceiling(TotalDays / 31);
        }
    }

    public DataTable callingLog;

    public int StartHour = 0;
    public int EndHour = 0;
    public double MaxValue = 0;
    public double MaxPercent = 0;

    public UsageDomainTypeEnum UsageType
    {
        get
        {
            return string.IsNullOrEmpty(PhoneNum) ? UsageDomainTypeEnum.PROVIDER : UsageDomainTypeEnum.INDIVIDUAL;
        }
    }

    public DomainUsagePro(DataTable callingLog, ProviderEnum provider, string phoneNumber)
    {
        cFromNo = callingLog.Columns["from_no"];
        cFromNet = callingLog.Columns["from_net"];
        cToNo = callingLog.Columns["to_no"];
        cToNet = callingLog.Columns["to_net"];
        cDate = callingLog.Columns["date"];
        cTime = callingLog.Columns["time"];
        cHour = callingLog.Columns["hour"];
        cMin = callingLog.Columns["min"];

        this.callingLog = callingLog;
        this.Provider = provider;
        this.PhoneNum = phoneNumber == null ? "" : phoneNumber.Trim();
    }

    public void Analysis()
    {
        HourUse = new double[24];
        Total = 0;


        if (UsageType == UsageDomainTypeEnum.PROVIDER)
        {
            if (Provider == ProviderEnum.All || Provider == ProviderEnum.OTHER)
            {
                CalAllProviders();
            }
            else
            {
                CalProvider();
            }
        }
        else
        {
            CalIndividual();
        }

        //for (int i = 0; i < 24; i++)
        //{
        //    mining(i);
        //}

        //MaxPercent = Math.Round(MaxValue / Total, 2);

        doMining();
    }

    //New approch
    public bool[] HourBoolean = new bool[24];
    public double AllScore = 0.0;
    public double ThisScore = 0.0;
    public double MaxSup = 0.6;

    private void doMining()
    {
        double max = HourUse.Max();
        HourBoolean = new bool[24];
        int length = Math.Min(HourUse.Length, HourBoolean.Length);
        ThisScore = 0.0;

        double subScore = max * MaxSup;

        for (int i = 0; i < length; i++)
        {
            if (HourUse[i] >= subScore)
            {
                HourBoolean[i] = true;
                ThisScore += HourUse[i];
            }
            else
            {
                HourBoolean[i] = false;
            }
        }

        AllScore = HourUse.Sum();
    }

    public void CalAllProviders()
    {
        var callingLinq = from myRow in callingLog.AsEnumerable()
                          select new
                          {
                              CallTo = myRow.Field<string>(cToNo),
                              NetTo = myRow.Field<string>(cToNet),
                              OnDate = myRow.Field<DateTime>(cDate),
                              AtTime = myRow.Field<double>(cTime),
                              InHour = myRow.Field<int>(cHour),
                              Minutes = myRow.Field<int>(cMin),
                          };

        //TotalDays = ((from myRow in callingLinq
        //              select new
        //              {
        //                  date = myRow.OnDate.ToShortDateString()
        //              }).Distinct()).Count();

        var firstDate = callingLinq.Min(r => r.OnDate);
        var lastDate = callingLinq.Max(r => r.OnDate);

        TotalDays = (lastDate - firstDate).Days +1;

        foreach (var x in callingLinq)
        {
            DateTime time = new DateTime();
            time = time.AddHours(x.OnDate.Hour);

            HourUse[time.Hour]++;
        }

        Total = HourUse.Average();

    }

    public void CalProvider()
    {
        HourUse = new double[24];
        RawTotal = new Dictionary<string, double[]>();

        string provider = "ais";
        switch (Provider)
        {
            case ProviderEnum.AIS: { provider = "ais"; } break;
            case ProviderEnum.DTAC: { provider = "dtac"; } break;
            case ProviderEnum.TMOVE: { provider = "tmove"; } break;
        }

        var callingLinq = from myRow in callingLog.AsEnumerable()
                          select new
                          {
                              CallTo = myRow.Field<string>(cToNo),
                              NetTo = myRow.Field<string>(cToNet),
                              OnDate = myRow.Field<DateTime>(cDate),
                              AtTime = myRow.Field<double>(cTime),
                              InHour = myRow.Field<int>(cHour),
                              Minutes = myRow.Field<int>(cMin),
                          };

        //TotalDays = ((from myRow in callingLinq
        //              select new
        //              {
        //                  date = myRow.OnDate.ToShortDateString()
        //              }).Distinct()).Count();

        var firstDate = callingLinq.Min(r => r.OnDate);
        var lastDate = callingLinq.Max(r => r.OnDate);

        TotalDays = (lastDate - firstDate).Days + 1;

        foreach (var x in callingLinq)
        {
            DateTime time = new DateTime();
            time = time.AddHours(x.OnDate.Hour);

            if (!RawTotal.ContainsKey(x.NetTo))
            {
                RawTotal.Add(x.NetTo, new double[24]);
            }

            RawTotal[x.NetTo][time.Hour]++;
        }

        HourUse = RawTotal.ContainsKey(provider) ? RawTotal[provider] : HourUse;


        Total = 0.0;
        foreach (string key in RawTotal.Keys)
        {
            Total += RawTotal[key].Average();
        }

        if (RawTotal.Count > 0)
        {
            Total = Total / RawTotal.Count();
        }
    }

    public void CalIndividual()
    {
        HourUse = new double[24];
        RawTotal = new Dictionary<string, double[]>();


        var callingLinq = from myRow in callingLog.AsEnumerable()
                          select new
                          {
                              CallTo = myRow.Field<string>(cToNo),
                              NetTo = myRow.Field<string>(cToNet),
                              OnDate = myRow.Field<DateTime>(cDate),
                              AtTime = myRow.Field<double>(cTime),
                              InHour = myRow.Field<int>(cHour),
                              Minutes = myRow.Field<int>(cMin),
                          };

        //TotalDays = ((from myRow in callingLinq
        //              select new
        //              {
        //                  date = myRow.OnDate.ToShortDateString()
        //              }).Distinct()).Count();

        var firstDate = callingLinq.Min(r => r.OnDate);
        var lastDate = callingLinq.Max(r => r.OnDate);

        TotalDays = (lastDate - firstDate).Days + 1;
        
        string pro = "";

        foreach (var x in callingLinq)
        {
            DateTime time = new DateTime();
            time = time.AddHours(x.OnDate.Hour);

            if (!RawTotal.ContainsKey(x.CallTo))
            {
                RawTotal.Add(x.CallTo, new double[24]);
            }

            RawTotal[x.CallTo][time.Hour]++;

            if (x.CallTo == PhoneNum)
            {
                pro = x.NetTo;
            }
        }

        HourUse = RawTotal.ContainsKey(PhoneNum) ? RawTotal[PhoneNum] : HourUse;

        switch (pro)
        {
            case "ais":
                {
                    Provider = ProviderEnum.AIS;
                } break;
            case "dtac":
                {
                    Provider = ProviderEnum.DTAC;
                } break;
            case "tmove":
                {
                    Provider = ProviderEnum.TMOVE;
                } break;
        }

        Total = 0.0;
        foreach (string key in RawTotal.Keys)
        {
            Total += RawTotal[key].Average();
        }

        if (RawTotal.Count > 0)
        {
            Total = Total / RawTotal.Count();
        }
    }

    private void mining(int h1)
    {
        int h0 = h1 - 1;
        int h2 = h1 + 1;
        h0 = h0 < 24 ? h0 : h0 - 24;
        h0 = h0 >= 0 ? h0 : h0 + 24;
        h1 = h1 < 24 ? h1 : h1 - 24;
        h2 = h2 < 24 ? h2 : h2 - 24;

        double x0 = HourUse[h0];
        double x1 = HourUse[h1];
        double x2 = HourUse[h2];

        double minsup = Math.Max(x0, Math.Max(x1, x2)) * Nearest;
        double stat = 0;

        int st;
        int en;

        if (x0 > minsup && x1 > minsup && x2 > minsup)
        {
            stat = x0 + x1 + x2;
            st = h0;
            en = h2;
        }
        else if (x0 > minsup && x1 > minsup)
        {
            stat = x0 + x1;
            st = h0;
            en = h1;
        }
        else if (x1 > minsup && x2 > minsup)
        {
            stat = x1 + x2;
            st = h1;
            en = h2;
        }
        else if (x1 > minsup)
        {
            stat = x1;
            st = h1;
            en = h1;
        }
        else
        {
            return;
        }


        if (MaxValue < stat)
        {
            MaxValue = stat;
            StartHour = st;
            EndHour = en + 1;

        }

    }

    public override string ToString()
    {
        string result = "Calling to {0} since {1} until {2} by taking {3} times/day ( of average use {4} )";
        string a0 = "";
        string a1 = StartHour + ":00";
        string a2 = (EndHour) + ":00";
        string a3 = Math.Round(MaxValue / TotalDays, 2).ToString();
        string a4 = Math.Round(Total / TotalDays, 2).ToString();
        string a5 = MaxPercent.ToString();

        switch (UsageType)
        {
            case UsageDomainTypeEnum.PROVIDER:
                {
                    switch (Provider)
                    {
                        case ProviderEnum.All: { a0 = "All Provider"; } break;
                        case ProviderEnum.AIS: { a0 = "AIS"; } break;
                        case ProviderEnum.DTAC: { a0 = "DTAC"; } break;
                        case ProviderEnum.TMOVE: { a0 = "TrueMove"; } break;
                    }
                } break;
            case UsageDomainTypeEnum.INDIVIDUAL:
                {
                    a0 = PhoneNum;
                } break;
        }


        return string.Format(result, a0, a1, a2, a3, a4, a5);
    }

    public int CompareTo(DomainUsagePro other)
    {
        //return this.MaxPercent.CompareTo(other.MaxPercent);

        return this.ThisScore.CompareTo(other.ThisScore);
    }

}